-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

-- Description: 升级hpm包
local client = require 'bios.client'
local ctx = require 'mc.context'
local log = require 'mc.logging'
local skynet = require 'skynet'

local M3Verify = {}

local SWITCH_TO_PCH<const> = 0
local SWITCH_TO_BMC<const> = 1
local BIOS_TYPE<const> = 1
local VERIFY_RETRY_CNT_ARM<const> = 50
local VERIFY_RETRY_INTERVAL<const> = 20
local VERIFY_RESULT_SUCCESS<const> = 0
local VERIFY_RESULT_FAILED<const> = 1
local M3_RETRY_TIMES<const> = 100
local CHANNEL_RETRY_TIMES<const> = 10
local TRIGGER_RETRY_TIMES<const> = 10
local VERIFY_PATH<const> = '/bmc/kepler/Managers/1/SOC/SecureCore'
local VERIFY_INTF<const> = 'bmc.kepler.Managers.SOC.SecureCore.FirmwareVerification'

local function wait_util_m3_startup()
    local times = M3_RETRY_TIMES
    while times > 0 do
        times = times - 1
        local m3_objs = client:GetFirmwareVerificationObjects()
        local _, m3_obj = next(m3_objs)
        if m3_obj then
            return m3_obj
        end
        skynet.sleep(20)
    end
    return nil
end

local function get_verify_res(verify_obj)
    for _ = 1, VERIFY_RETRY_CNT_ARM do
        local ok, res = verify_obj.pcall:GetBiosVerifyResult(ctx.new())
        if not ok then
            log:error('[bios]verify service: get verify res fail.')
            return false
        end

        if res == VERIFY_RESULT_SUCCESS then
            log:info('[bios]verify service: The BIOS firmware is verified successfully by Secure Core.')
            return true
        elseif res == VERIFY_RESULT_FAILED then
            log:info('[bios]verify service: Failed to verify the BIOS firmware ' ..
                'and the backup version will be restored.')
            return false
        else
            log:info('[bios]verify service: wait verify finish.%d', res)
        end
        skynet.sleep(VERIFY_RETRY_INTERVAL)
    end
    return true
end

local function set_channel(verify_obj, channel)
    for _ = 1, CHANNEL_RETRY_TIMES do
        local ok, _ = verify_obj.pcall:SetSpiMuxChannel(ctx.new(), channel)
        if ok then
            return true
        end
        skynet.sleep(50)
    end
    return false
end

local function trigger_verify(verify_obj)
    for _ = 1, TRIGGER_RETRY_TIMES do
        local ok, _ = verify_obj.pcall:StartBiosVerify(ctx.new(), BIOS_TYPE)
        if ok then
            return true
        end
        skynet.sleep(50)
    end
    return false
end

function M3Verify.verify()
    log:info('[bios]verify service: start verify.')
    local verify_obj = wait_util_m3_startup()
    if not verify_obj then
        log:error('[bios]verify service: get verify obj fail.')
        return false
    end
    log:info('[bios]verify service: verify component started.')
    -- 切换总线到bmc
    local ok = set_channel(verify_obj, SWITCH_TO_BMC)
    if not ok then
        log:error('[bios]verify service: set channel to bmc fail.')
        return false
    end
    log:info('[bios]verify service: set channel to bmc success.')
    ok = trigger_verify(verify_obj)
    if not ok then
        log:error('[bios]verify service: trigger verify fail.')
        return false
    end
    log:info('[bios]verify service: trigger verify success.')

    local res = get_verify_res(verify_obj)
    if res then
        -- 切换总线到pch
        ok = set_channel(verify_obj, SWITCH_TO_PCH)
        if not ok then
            log:error('[bios]verify service: set channel to pch fail.')
            return false
        end
        log:info('[bios]verify service: set channel to pch success.')
        return true
    else
        log:error('[bios]verify service: verify fail.')
        return false
    end
end

return M3Verify